Skip to content

Record what the external validation established (#437) - #440

Merged
jdatcmd merged 2 commits into
commandprompt:mainfrom
ChronicallyJD:docs/437-external-report
Aug 6, 2026
Merged

Record what the external validation established (#437)#440
jdatcmd merged 2 commits into
commandprompt:mainfrom
ChronicallyJD:docs/437-external-report

Conversation

@ChronicallyJD

Copy link
Copy Markdown
Collaborator

Closes #437. @jdatcmd for review.

Four documentation changes from the external validation report, each backed by a
measurement rather than by the report's word. All 12 docs pages pass docs_style.sh.

1. aarch64 is no longer untested

docs/limitations.md said every recorded result comes from x86_64 and the suites have
never run on aarch64 (#242). The independent gate ran the full suite on a Graviton3 host
and every suite passed, with the same per-suite result as x86_64. The big-endian half of
that sentence is still true and stays.

The owner asked that the arm against x86 timing ratios not be published, so they are
not here. Graviton being slower on scan paths is not a finding anyone needs. The
correctness update is a different claim and it is in.

2. stripe_row_limit is documented as the fetch-cost knob, and chunk_group_row_limit is named as not being it

The report recommends chunk_group_row_limit for point-lookup-heavy tables. It does
nothing.
Isolated one knob at a time on a 511 MB blob-heavy fixture:

configuration point lookup
defaults 245.4 ms
chunk_group_row_limit 1000 only 252.3 ms, no effect
stripe_row_limit 10000 only 19.3 ms

Both the reporter and my own first measurement changed the two together and could not tell
them apart. The docs now name the right one and say the other is not it, because the advice
as published would send people to tune a knob that does not move this.

3. The knob's cost is stated with its benefit

stripe_row_limit size point lookup full aggregate scan
150000 (default) 527.0 MB 244.1 ms 38.7 ms
10000 527.3 MB 18.5 ms 35.4 ms
2000 529.4 MB 5.6 ms 37.9 ms

Size grows 0.4 percent, scan throughput does not change, and there is a floor: a
selective range query was slower at 2000 than at 10000, because a smaller row group makes
more metadata to read. A tuning knob documented without its cost is a trap, so the floor is
in the same paragraph as the win.

4. Cross-architecture portability, stated as narrowly as the format supports

This is the one I would look at hardest.

The report moved a data directory from x86_64 to aarch64 and read it correctly, and
suggested a docs line. Both of those are little-endian, so that test cannot distinguish
"portable" from "host-endian and both hosts agree."

It is host-endian, deliberately, and both the spec and the header say so:

design/NATIVE_FORMAT_AND_INTERFACE_SPEC.md:11
  Little-endian hosts are assumed for the layout below

src/columnar.h:57
  Integers are host-endian (little-endian hosts assumed, spec 3).

PgColumnarEncodeValue writes fixed-width values with store_att_byval, which is the raw
host representation. The one #ifdef WORDS_BIGENDIAN in the tree
(columnar.h:919-926) mirrors PostgreSQL's in-memory varlena header layout and has nothing
to do with the on-disk format.

So the docs say same byte order, not "across architectures". They also say
PostgreSQL's own heap format follows the same rule, because that is the point: a columnar
table is no more restricted than the rest of the cluster, and a reader should not come away
thinking this is a pgColumnar limitation.

5. A "when not to use pgColumnar" section

docs/index.md, from the reporter's ledger numbers and attributed to them. It keeps the two
rows where columnar still wins (1.27x smaller, 2.3x faster on the narrow aggregate), because
a section listing only losses is not the honest version of it.

Deliberately absent

Logical replication limitations. #439 is writing those and I am not going to collide with it.

🤖 Generated with Claude Code

)

Four changes, each backed by a measurement rather than by the report alone.

aarch64 is no longer untested. limitations.md said every recorded result comes
from x86_64 and the suites have never run on aarch64 (commandprompt#242). An independent gate
on a Graviton3 host passed every suite with the same per-suite result as x86_64.
The big-endian half of that sentence stays true and stays.

The owner asked that the arm against x86 timing ratios not be published, so they
are not here. The correctness update is a different claim and it is.

stripe_row_limit is documented as the setting that governs fetch-by-index cost,
with the sweep and with its cost. chunk_group_row_limit is named explicitly as
NOT the setting for this, because the external report recommended it and it does
nothing here: isolated one knob at a time, chunk_group alone moved a point lookup
from 245.4 ms to 252.3 ms, and stripe_row_limit alone moved it to 19.3 ms.

The floor is stated with the benefit. Below 10000 rows a selective range query
regresses, because a smaller row group makes more metadata to read. A tuning knob
documented without its cost is a trap.

Cross-architecture portability is stated as narrowly as the format supports. The
native format stores integers in HOST byte order, which the format specification
says at line 11 and columnar.h:57 repeats. The validation moved a data directory
from x86_64 to aarch64 and read it correctly, but both are little-endian, so that
test cannot distinguish portable from host-endian. The docs now say same byte
order, note that PostgreSQL's own heap format has the same rule, and say a
big-endian move is unsupported and untested.

index.md gains a "when not to use pgColumnar" section from the reporter's ledger
numbers, attributed to them. It keeps the two rows where columnar still wins,
because a section that lists only losses is not the honest version.

Logical replication limitations are deliberately absent: commandprompt#439 is writing those.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqprqkCXuH8SegiZejE1Tw
@ChronicallyJD

Copy link
Copy Markdown
Collaborator Author

Added one more, found while verifying the portability claim.

docs/limitations.md's Host architecture section said:

The rest of the extension runs on any architecture PostgreSQL supports.

The code does. A data directory does not. That sentence sat in the one section a reader consults before moving a cluster, and it contradicted the portability text this PR was adding to administration.md.

Both now say the same thing: same byte order, the same rule PostgreSQL's own heap format follows, and a big-endian move unsupported and untested. The section also picks up the aarch64 update, since that is where a reader looks for it.

Separately: docs/limitations.md on main carries three merge conflict markers, live on the site, which every check in docs_style.sh passed. That is #442, kept separate from this PR because a broken published page should not wait behind a review of new prose. This branch merges cleanly with it.

@ChronicallyJD
ChronicallyJD force-pushed the docs/437-external-report branch from 5d53676 to 2cec721 Compare August 6, 2026 00:11
…at does not (commandprompt#437)

The Host architecture section said "The rest of the extension runs on any
architecture PostgreSQL supports." The CODE does. A DATA DIRECTORY does not.

The native format stores multi-byte values in host byte order. The format
specification says so at line 11, and columnar.h:57 repeats it. So the sentence
was true about the binary and misleading about the data, in the one section a
reader consults before moving a cluster.

This is the same claim added to administration.md in the previous commit, in the
section that contradicted it. Both now say the same thing: same byte order, the
same rule PostgreSQL's own heap format follows, and a big-endian move
unsupported and untested.

It also picks up the aarch64 update, since this section is where a reader looks
for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqprqkCXuH8SegiZejE1Tw
@ChronicallyJD
ChronicallyJD force-pushed the docs/437-external-report branch from 2cec721 to 3e4827d Compare August 6, 2026 00:12

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the load-bearing claim independently, on a different fixture. It holds.

The claim that would send people the wrong way if wrong is "stripe_row_limit governs
fetch cost, chunk_group_row_limit does not". I isolated one knob at a time on my own
fixture, 200,000 rows, median of five random point lookups:

configuration size point lookup
defaults 4480 kB 111 ms
chunk_group_row_limit=1000 only 4480 kB 133 ms, no effect
stripe_row_limit=10000 only 4584 kB 23 ms
stripe_row_limit=2000 only 5224 kB 12 ms

Same conclusion as yours, and worth more than a repeat because my fixture compresses and
yours does not
: 4.5 MB against your 527 MB for a comparable row count, because my blob is
hex and yours was incompressible. The knob's behaviour is the same on both shapes, which is
a stronger claim than either measurement alone.

Documenting a knob as ineffective is exactly the kind of statement that deserves a second
fixture, since a reader will act on it and never re-measure.

One number a reader may over-generalise

Your text says of the smaller row group:

Size grows by 0.4 percent at 2000 rows.

On incompressible data, which the paragraph does state. On my compressible fixture the
same setting cost 16 percent (4480 kB to 5224 kB). Both are correct for their data, and
the reason is the same one your ClickBench prefix finding turns on: fixture shape decides
the answer.

Not blocking, and the fixture is named right above the table. A clause noting the overhead
is larger when the data compresses would stop someone reading 0.4 percent as a general
property. Your call.

The rest

"When not to use pgColumnar" is the most valuable section in this PR. A 200x point
lookup and a 24x ordered segment scan, published beside the 2.3x aggregate win, is worth
more to a prospective user than any benchmark that only shows the good shapes. Keeping the
last two rows in the same table, with "read the last two rows with the others", is the
right presentation.

The aarch64 correction is properly scoped. Suites pass, byte order unchanged, big-endian
still untested and still said so. Withholding the arm-versus-x86 timing ratios at the
owner's request is right too: correctness and performance are different claims and only one
was established.

All 12 pages pass docs_style.sh. Approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Documentation from the external validation report: the right tuning knob, when not to use pgColumnar, aarch64, and cross-architecture portability

2 participants